package com.hailiang.saas.auth.config;

import com.hailiang.saas.auth.common.Constant;
import com.hailiang.saas.auth.filter.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

/**
 * @author : pangfuzhong
 * @description
 * @date : 2021/9/27 11:21
 */
@EnableWebSecurity
public class ServerWebSecurityConfigAdapter extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthenticationConfig authenticationConfig;

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 配置给予UserDetailService认证(即账号和密码方式)
        auth.userDetailsService(this.authenticationConfig.getUserDetailsService()).passwordEncoder(new BCryptPasswordEncoder(31));
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // 忽略options预请求, spring security将会忽略options类型的浏览器发送的预请求
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 允许跨域访问
        // http.cors();
        // 关闭跨站请求伪造
        http.csrf().disable();
        // 关闭session(前后端分离无需使用到session)
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 认证过滤条件
        http.authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS, Constant.MATCH_ALL_PATH).permitAll()
            // 登陆认证url，token认证url，验证码获取url 请求不进行认证，网关放行, 认证中心拦截特殊处理
            .antMatchers(HttpMethod.POST, this.authenticationConfig.getLoginAuthUrl(), this.authenticationConfig.getTokenAuthUrl(), this.authenticationConfig.getVerificationAuthUrl(), this.authenticationConfig.getWhiteListUrl()).permitAll()
            .anyRequest().authenticated(); // 没有定义的请求，所有的角色都可以访问（tmp也可以）

        // 1、token 认证过滤器
        http.addFilter(new TokenAuthenticationFilter(authenticationManager(), new VerificationAuthenticationFailEntryPoint(), this.authenticationConfig));
        // 2、web_browser password 认证过滤器
        PasswordAuthFilter passwordAuthFilter = new PasswordAuthFilter(this.authenticationConfig);
        passwordAuthFilter.setUsernameParameter(Constant.USERNAME);
        passwordAuthFilter.setPasswordParameter(Constant.PASSWORD);
        ProviderManager providerManager = (ProviderManager) authenticationManager();
        providerManager.getProviders().stream()
            .filter(authenticationProvider -> authenticationProvider instanceof DaoAuthenticationProvider)
            .forEach(authenticationProvider -> {
                ((DaoAuthenticationProvider) authenticationProvider).setHideUserNotFoundExceptions(Boolean.FALSE);
            });
        passwordAuthFilter.setAuthenticationManager(providerManager);
        passwordAuthFilter.setAuthenticationSuccessHandler(null);
        passwordAuthFilter.setAuthenticationFailureHandler(null);
        http.addFilterAfter(passwordAuthFilter, TokenAuthenticationFilter.class);
        // 3、登出过滤器
        http.addFilterAfter(new UnifiedLogoutFilter(this.authenticationConfig), PasswordAuthFilter.class);
        // 4、web_browser verification 认证过滤器
        http.addFilterAfter(new VerificationAuthFilter(this.authenticationConfig), UnifiedLogoutFilter.class);

        //开启记住我功能,session和cookie实现，默认两周时间
        //http.rememberMe().rememberMeParameter("rememberme");
    }

    //@Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 携带服务器端验证后允许的跨域请求域名, 即 Access-Control-Allow-Origin 参数, 即本服务允许该参数指定的域名进行跨域访问
        configuration.addAllowedOrigin("*");
        // 服务端告知浏览器当前端配置了withCredentials属性为true时，在跨域访问时带上认证信息(cookie) 即 Access-Control-Allow-Credentials
        configuration.setAllowCredentials(true);
        // 服务端告知浏览器可以在实际发送跨域请求时，支持的请求类型, 即 Access-Control-Allow-Headers
        configuration.addAllowedHeader(CorsConfiguration.ALL);
        // 服务端告知浏览器可以在实际发送跨域请求时，跨域请求支持的方法, 即 Access-Control-Allow-Methods
        configuration.addAllowedMethod(CorsConfiguration.ALL);
        // 当不同源的的网站所在浏览器发起的请求都按照此跨域配置进行
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
